# 创建时间:2024/4/14 上午3:49
# 创建人:天霄
# 基于 Python 3.11
# ========================================
# 使用 SQLAlchemy 定义数据库模型
# ========================================
from dataclasses import dataclass
from typing import Tuple

from vars import CONST
from sqlalchemy import create_engine, Column, Integer, String, Text, ForeignKey, Boolean, Row
from sqlalchemy.orm import sessionmaker, declarative_base, DeclarativeBase, scoped_session

__all__ = ['dbModel', 'Row']

Base: DeclarativeBase = declarative_base()


class BaseFunc:
    def to_dict(self) -> dict:
        return {c.name: getattr(self, c.name, None) for c in self.__table__.columns}
    
    
# 角色信息表
class Role(Base, BaseFunc):
    """角色信息表"""
    __tablename__ = 'role'
    role_id = Column(Integer, primary_key=True)  # 角色ID
    name = Column(String(255), nullable=False)  # 名字
    job = Column(String(255), nullable=False)  # 职业（hero-领袖，soldier-军士，m-机甲，boss-BOSS，enemy-敌人，o-障碍物）
    sex = Column(Integer, nullable=False)  # 性别（0-未知，1-男，2-女，3-其他）
    cv_cn = Column(String(255))  # 中文配音cv
    cv_jp = Column(String(255))  # 日语配音cv
    age = Column(String(255))  # 年龄
    constellation = Column(String(255))  # 星座
    birthday = Column(String(255))  # 生日
    blood_type = Column(String(255))  # 血型
    height = Column(Integer)  # 身高
    weight = Column(Integer)  # 体重
    cup = Column(String(255))  # cup
    interest = Column(Text)  # 兴趣
    dream = Column(Text)  # 将来梦想
    like = Column(Text)  # 喜欢的事物
    dislike = Column(Text)  # 讨厌的事物
    phantasy = Column(Text)  # 憧憬的人


# 角色代号表
class RoleCode(Base, BaseFunc):
    """角色代号表"""
    __tablename__ = 'role_code'
    code_id = Column(Integer, primary_key=True)  # 代号ID
    role_id = Column(Integer, ForeignKey('role.role_id'), nullable=False)  # 角色ID【@role表】
    code = Column(String(255), nullable=False)  # 代号
    code_type = Column(Integer, nullable=False)  # 代号指代的类型（1-皮肤，2-服装，3-头像，4-声音，0-无）
    status = Column(Integer, nullable=False)  # 状态（1-实装，0-未实装）
    has_text = Column(Boolean)  # 是否含有text人名图（1-是，0-否）
    has_img = Column(Boolean)  # 是否含有立绘（1-是，0-否）
    has_sound = Column(Boolean)  # 是否含有声音（1-是，0-否）
    suit = Column(String(255))  # 套装名，军士/服装系列/头像系列，如：校园、偶像、萌化头像等。是同一类的角色。有code并不一定有军士，可能仅出了头像。
    describe = Column(Text)  # 备注和描述
    cloth_icon_id = Column(Integer)  # 服装图标ID【@icon表】


# 军士表
class Soldier(Base, BaseFunc):
    """军士表，包括军士领袖和敌兵"""
    __tablename__ = 'soldier'
    sid = Column(Integer, primary_key=True)  # 军士ID或领袖ID或敌人ID
    role_id = Column(Integer, ForeignKey('role.role_id'), nullable=False)  # 角色ID【@role表】
    code = Column(String(255))  # 代号【@role_code表】
    name = Column(String(255), nullable=False)  # 名字
    rare = Column(String(255))  # 稀有度（1-白，2-绿，3-紫，4-金）
    arms = Column(Integer)  # 兵种（1-盾-装甲，2-弹-敢死，3-毒-生化，4-特遣，5-指挥，6-boss，7-敌兵）
    place = Column(Integer)  # 站位（1-前，2-中，3-后，4-指挥位，5-空中）
    describe = Column(Text)  # 描述
    status = Column(Integer, nullable=False)  # 状态（1-实装，0-未实装）
    main = Column(Integer, nullable=False)  # 是否是主军士（1-是，0-否）。一个角色可能有多个军士，用于标记最初军士。


# 立绘表
class CharImage(Base, BaseFunc):
    """立绘/半身绘/表情/头像图片资源"""
    __tablename__ = 'charimage'
    image_id = Column(Integer, primary_key=True)  # 图片ID
    code_id = Column(Integer, ForeignKey('role_code.code_id'), nullable=False)  # 代号ID【@role_code表】
    file = Column(String(255), nullable=False)  # 图片名=job+nature+face+facenum+imgtype+.png
    job = Column(String(255), nullable=False)  # 职业（soldier/hero/boss等）
    nature = Column(String(255), nullable=False)  # 性质（urs\s\rs等）
    code = Column(String(255), nullable=False)  # 代号（冗余字段）【@role_code表】
    imgtype = Column(String(255), nullable=False)  # 图片具体类型（half/halfhead/head/full等）
    face = Column(String(255))  # 特殊表情类，非必填（如shy/angry/smile等）
    facenum = Column(Integer)  # 表情序号，非必填
    describe = Column(Text)  # 描述
    status = Column(Integer, nullable=False)  # 状态（1启用，0不启用）


# 音频表
class Sound(Base, BaseFunc):
    """音频表"""
    __tablename__ = 'sound'
    sound_id = Column(Integer, primary_key=True)  # 声音ID
    code_id = Column(Integer, ForeignKey('role_code.code_id'), nullable=False)  # 代号ID【@role_code表】
    language = Column(String(255), nullable=False)  # 语言 （仅cn/jp）
    path = Column(String(255), nullable=False)  # 声音文件总目录下的相对路径
    scene = Column(String(255), nullable=False)  # 场景（activity/battle/char/common/favor/story/ui）
    favor_type = Column(String(255))  # 场景为favor时，该目录下指定目录名（daily/gift/touch）
    file = Column(String(255), nullable=False)  # 文件名，无后缀
    event = Column(String(255), nullable=False)  # 事件（get/gift/touch等）
    nature = Column(String(255))  # 性质（urs\s\rs等）
    code = Column(String(255))  # 代号（冗余字段）【@role_code表】
    way = Column(String(255))  # 用途/部位（仅event=touch/daily时，有一部分音频有此值）
    num = Column(String(255))  # 末尾序号
    sound_content = Column(Text)  # 语音本身的文字内容
    content_id = Column(Integer, ForeignKey('content.content_id'))  # 文本ID【@content表】
    describe = Column(Text)  # 描述
    status = Column(Integer, nullable=False)  # 状态（1启用，0不启用）


# 音频表情关系表（未定）
class SoundFace(Base, BaseFunc):
    """音频表情关系表"""
    __tablename__ = 'sound_face'
    sound_id = Column(Integer, ForeignKey('sound.sound_id'), primary_key=True)  # 声音ID【@sound表】
    image_id = Column(Integer, ForeignKey('charimage.image_id'), primary_key=True)  # 表情图片ID【@charimage表】
    describe = Column(Text)  # 描述
    valid_flag = Column(Integer)  # 是否有效


# 人名图片表
class TextImg(Base, BaseFunc):
    """人名图片表"""
    __tablename__ = 'text_img'
    text_img_id = Column(Integer, primary_key=True)  # 文字图片ID
    code_id = Column(Integer, ForeignKey('role_code.code_id'), nullable=False)  # 代号ID【@role_code表】
    file = Column(String(255), nullable=False)  # 图片名="name"+nature+vertical+code+.png
    vertical = Column(String(255), nullable=False)  # 纵向标识（仅name/namevertical）
    nature = Column(String(255), nullable=False)  # 性质（b/s/m/h等）
    code = Column(String(255), nullable=False)  # 代号（冗余字段）【@role_code表】
    describe = Column(Text)  # 描述
    status = Column(Integer, nullable=False)  # 状态（1启用，0不启用）


# 角色记事表
class RoleRecord(Base, BaseFunc):
    """角色记事表"""
    __tablename__ = 'role_record'
    role_rec_id = Column(Integer, primary_key=True)  # 角色记录ID
    role_id = Column(Integer, ForeignKey('role.role_id'), nullable=False)  # 角色ID【@role表】
    record_type = Column(Integer, nullable=False)  # 记录类型（1-犯罪记录，2-角色故事，3-私密记事）
    title = Column(String(255))  # 标题
    content_id = Column(Integer, ForeignKey('content.content_id'))  # 内容ID


# 文本表
class Content(Base, BaseFunc):
    """文本表"""
    __tablename__ = 'content'
    content_id = Column(Integer, primary_key=True)  # 内容ID
    type = Column(Integer, nullable=False)  # 类型（1-犯罪记录，2-角色故事，3-私密记事，4-语音文本）
    content = Column(Text, nullable=False)  # 文本内容


# 图标表
class Icon(Base, BaseFunc):
    """图标表"""
    __tablename__ = 'icon'
    icon_id = Column(Integer, primary_key=True)  # 图标ID
    type = Column(String(255), nullable=False)  # 类型（icon的下级目录）
    nature = Column(String(255), nullable=False)  # 性质（如appare-服装、勋章、道具等）
    path = Column(String(255), nullable=False)  # 路径，从/assets/res/icon开始
    describe = Column(Text)  # 描述和备注


class ConnectDB:
    def __enter__(self):
        engine = create_engine(f'sqlite:///{CONST.PATH.DATA_ROOT / "SenjoTwintail.db"}')
        DBSession = scoped_session(sessionmaker(bind=engine))
        self.session = DBSession()
        return self.session

    def __exit__(self, exc_type, exc_val, exc_tb):
        # print('已关闭连接'.center(60, '-'))
        self.session.close()


@dataclass(frozen=True)
class DBModel:
    """数据库模型：所有表"""
    role = Role
    role_code = RoleCode
    soldier = Soldier
    charimage = CharImage
    sound = Sound
    sound_face = SoundFace
    text_img = TextImg
    role_record = RoleRecord
    content = Content
    icon = Icon

    @staticmethod
    def withConnect():
        """连接数据库"""
        return ConnectDB()

    @staticmethod
    def to_dict(one_data: Base | Row) -> dict | Tuple[dict]:
        """将一条数据对象转为字典"""
        if isinstance(one_data, Row):
            return tuple([one.to_dict() for one in one_data])
        elif isinstance(one_data, BaseFunc):
            return one_data.to_dict()
        raise TypeError(f'只能转换数据库表格类，然而实际为 {type(one_data)}')


dbModel = DBModel()


if __name__ == '__main__':
    print(Base.metadata.tables)

    def print_datas(datas):
        for data in datas:
            print(dbModel.to_dict(data))

    with dbModel.withConnect() as session:

        datas = session.query(dbModel.charimage, dbModel.role_code).filter(
            dbModel.charimage.code == dbModel.role_code.code,
            dbModel.charimage.code == 'hn').all()
        print_datas(datas)

        # print('charimage'.center(60, '-'))
        # datas = session.query(dbModel.charimage).filter(dbModel.charimage.code == 'hn')
        # print(type(next(iter(datas))), type(next(iter(datas.all()))))
        # print(next(iter(datas)), next(iter(datas.all())))
        # print()
        # print_datas(datas)
        #
        # print('role'.center(60, '-'))
        # datas = session.query(dbModel.role).filter(dbModel.role.name == '奥莉索')
        # print_datas(datas)
        #
        # print('role_code'.center(60, '-'))
        # datas = session.query(dbModel.role_code).filter(dbModel.role_code.code == 'qxjm')
        # print_datas(datas)
        #
        # print('role_record'.center(60, '-'))
        # datas = session.query(dbModel.role_record).filter(dbModel.role_record.role_rec_id == 1)
        # print_datas(datas)
        #
        # print('content'.center(60, '-'))
        # datas = session.query(dbModel.content).filter(dbModel.content.content_id == 17)
        # print_datas(datas)
        #
        # print('icon'.center(60, '-'))
        # datas = session.query(dbModel.icon).filter(dbModel.icon.icon_id == 16)
        # print_datas(datas)
        #
        # print('soldier'.center(60, '-'))
        # datas = session.query(dbModel.soldier).filter(dbModel.soldier.code == 'jm')
        # print_datas(datas)
        #
        # print('text_img'.center(60, '-'))
        # datas = session.query(dbModel.text_img).filter(dbModel.text_img.code_id == 1)
        # print_datas(datas)
        #
        # print('sound'.center(60, '-'))
        # datas = session.query(dbModel.sound).filter(dbModel.sound.code_id == 96)
        # print_datas(datas)


